Skip to content

Conversation

@aardvark179
Copy link
Contributor

More preparation for separating the concepts of scopes and scriptable objects.

Property descriptors as described in https://tc39.es/ecma262/#sec-property-descriptor-specification-type are not JavaScript objects, but merely internal Record objects with a particular set of fields. Meanwhile FromPropertyDescriptor creates a corresponding JavaScript object and sets a prototype based on the current realm.

While prototyping separating scopes and realms, and reducing where we relied on parent scopes it became clear that our conflation of these two objects made it impossible to get some aspects of the spec correct without altering many of our APIs to pass a scope through. We had already introduced a DescriptorInfo object to avoid potential deadlocks with slot map operations, so this PR expands its use to pretty much everywhere where the specs specifies a property descriptor.

@aardvark179 aardvark179 changed the title Refactor internal property descriptors to not be scriptable objects. Refactor internal property descriptors to not be ScriptableObjects. Oct 26, 2025
@aardvark179 aardvark179 force-pushed the aardvark179-property-operations-fix branch from 1245ba7 to 6fe3f0a Compare October 28, 2025 10:23
Copy link
Collaborator

@gbrail gbrail left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I can see how this simplifies things. But I can't help but think that some handy member functions on DescriptorInfo would reduce the need for all that "Boolean.TRUE.equals" code all over.

}

public static final class DescriptorInfo {
Object enumerable;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this code be cleaner overall if DescriptorInfo had accessors that validated the whole NOT_FOUND bit (isEnumerable, isConfigurable, etc)?

Object getter,
Object setter,
Object value) {
this.enumerable = enumerable;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it makes the code bigger but would you consider a "builder" pattern for this? I worry about being able to read the code when every DescriptorInfo constructor has six parameters of the same type in a row.

}
}

protected void checkPropertyDefinition(DescriptorInfo desc) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a member of the DescriptorInfo class?

return hasProperty(desc, "get") || hasProperty(desc, "set");
}

protected static boolean isAccessorDescriptor(DescriptorInfo desc) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a member of the DescriptorInfo class?

return !isDataDescriptor(desc) && !isAccessorDescriptor(desc);
}

protected static boolean isGenericDescriptor(DescriptorInfo desc) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Member of the class -- it's a static function that takes one argument...

return false;
}
if (Boolean.FALSE.equals(getProperty(desc, "enumerable"))) {
if (Boolean.FALSE.equals(desc.enumerable)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this code (and many places like it) be easier to read and follow if it could call "!descriptor.isEnumerable()" or if NOT_FOUND is a special third case, "descriptor.isNotEnumerable()"

@aardvark179
Copy link
Contributor Author

Thanks! I can see how this simplifies things. But I can't help but think that some handy member functions on DescriptorInfo would reduce the need for all that "Boolean.TRUE.equals" code all over.

I agree. I held off doing that initially because an extremely mechanical change was slightly easier to produce, but it is definitely more readable with a better API. After a little experimenting I'm erring towards something like:

boolean isEnumerable() // check for a simple value of true
boolean isEnumerable(boolean value) // check for an explicitly set value that matches
boolean hasEnumerable() // check that a value has been set

The reason for that is that there are a lot of cases where we care that an explicit value of FALSE has been set, and writing

desc.hasEnumerable() && !desc.isEnumerable()

is asking for bugs in the operator precedence.

@gbrail
Copy link
Collaborator

gbrail commented Nov 1, 2025

That makes sense -- I had been thinking that we'd need "isEnumerable" and "isNotEnumerable" but I think that your way is more Java-like!

@aardvark179 aardvark179 force-pushed the aardvark179-property-operations-fix branch from 6fe3f0a to 9a7546f Compare November 2, 2025 17:29
@gbrail
Copy link
Collaborator

gbrail commented Nov 6, 2025

Thanks -- looks good!

@gbrail gbrail merged commit 3dd4035 into mozilla:master Nov 6, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants